
package peerToPeerFacebook.proj;

import java.io.IOException;
import java.io.Serializable;

import peerToPeerFacebook.src.edu.washington.cs.cse490h.lib.PersistentStorageReader;
import peerToPeerFacebook.src.edu.washington.cs.cse490h.lib.PersistentStorageWriter;
import peerToPeerFacebook.src.edu.washington.cs.cse490h.lib.Utility;
// import java.util.ArrayList;

/**
 * Class implementing IFacebookMethods
 */
public class FacebookMethods implements IFacebookMethods
{

    /** Pending Friend Request File suffix */
    public static final String PendingFriendRequestsFileSuffix = "_pendingFriendRequests.txt";
    /** Message File suffix */
    public static final String MessagesFileSuffix = "_messages.txt";
    /** Logged In users file */
    public static final String LoggedInUsers = "LoggedInUser.txt";
    /** FBNode reference */
    private FBNode node;
    /** Username of the User logged In */
    private String userName;
    /** Format string for printing the output message onto the screen */
    private String format = "***************";

   /* *//**
     * Method that maps userNames to the servers which stores their account
     * information. It uses the first letter of the userName to map the names to
     * the appropriate servers
     * 
     * @param userName String username
     * @return the server ID of the server.
     *//*
    public int mapUser(String userName)
    {
        int serverID = -1;
        if (userName.toLowerCase().startsWith("a") || userName.toLowerCase().startsWith("b") ||
            userName.toLowerCase().startsWith("c") || userName.toLowerCase().startsWith("d") ||
            userName.toLowerCase().startsWith("e"))
        {
            serverID = 1;
        }
        if (userName.toLowerCase().startsWith("f") || userName.toLowerCase().startsWith("g") ||
            userName.toLowerCase().startsWith("h") || userName.toLowerCase().startsWith("i") ||
            userName.toLowerCase().startsWith("j"))
        {
            serverID = 2;
        }
        if (userName.toLowerCase().startsWith("k") || userName.toLowerCase().startsWith("l") ||
            userName.toLowerCase().startsWith("m") || userName.toLowerCase().startsWith("n") ||
            userName.toLowerCase().startsWith("o"))
        {
            serverID = 3;
        }
        if (userName.toLowerCase().startsWith("p") || userName.toLowerCase().startsWith("q") ||
            userName.toLowerCase().startsWith("r") || userName.toLowerCase().startsWith("s") ||
            userName.toLowerCase().startsWith("t"))
        {
            serverID = 4;
        }
        if (userName.toLowerCase().startsWith("u") || userName.toLowerCase().startsWith("v") ||
            userName.toLowerCase().startsWith("w") || userName.toLowerCase().startsWith("x") ||
            userName.toLowerCase().startsWith("y") || userName.toLowerCase().startsWith("z"))
        {
            serverID = 5;
        }
        return serverID;
    }
*/
    /**
     * Constructor of facebook Method class
     * 
     * @param node the reference FBNode
     */
    public FacebookMethods(FBNode node)
    {
        this.userName = "";
        this.node = node;
    }

    /**
     * A helper method which checks to see if the user is logged in.
     */
    boolean ensureUserIsLoggedIn()
    {
        userName = readLoggedInUsers();
        if ((userName.equals("")) && (readLoggedInUsers().equals("")))
            return false;
        else
            return true;
    }

    /**
     * A helper method for writing loginUserName to the LoggedUsers file
     */
    private void writeToLoggedInUser(String loginUserName)
    {
        PersistentStorageWriter tempWriter;
        try
        {
            tempWriter = this.node.getWriter(LoggedInUsers, false);
            tempWriter.write(loginUserName);
            tempWriter.close();
        }
        catch (IOException e)
        {
            System.out.println("\n\n Error writing user name to the disk....\n\n");
        }
    }

    /**
     * A helper method for reading logged User if any from the LoggedInUser file
     */
    private String readLoggedInUsers()
    {
        if (Utility.fileExists(node, LoggedInUsers))
        {
            PersistentStorageReader reader = null;
            StringBuilder result = new StringBuilder();
            String line;
            try
            {
                reader = node.getReader(LoggedInUsers);
                while ((line = reader.readLine()) != null)
                {
                    result.append(line);
                }
                reader.close();
            }
            catch (Exception e)
            {
                System.out.println("\n\n Failed to open the loggedIn Users file\n\n");
            }

            if (result.length() != 0)
                return result.toString();
        }
        return "";
    }

    /**
     * Method that represents the login functionality of facebook class, when
     * the user logs in, the user field is set to the username/login name given
     * by the user. It sets the userName field of the class to the user supplied
     * name. Also the userName is made persistent by saving it in a file. Which
     * is always checked to see if some user is already logged In.
     * 
     * @param loginUserName, the login name of the user.
     */
    public void login(String loginUserName)
    {
        String userNameInFile;
        assert loginUserName != "" : "The supplied Login Name cannot be null";

        if (userName != "")
        {
            // when the class member variable is not null
            if (this.userName.equalsIgnoreCase(loginUserName))
            {
                System.out.println("\n\nUser: " + userName + " is already logged in!\n\n");
            }
            else
            {
                System.out
                        .println("\n\nUser: " +
                                 userName +
                                 " is currently logged in. Please logout first if you wish to login as " +
                                 loginUserName + "\n\n");
            }
        }
        else
        {
            // when the class member variable is null, It may be possible that
            // the loggedInUser file contains the
            // logged in user, So lets check that also.
            assert userName.equals("") : "UserName should not be non-empty";
            userNameInFile = readLoggedInUsers();
            userName = userNameInFile;
            if (userNameInFile != "")
            {
                if (userNameInFile.equalsIgnoreCase(loginUserName))
                {
                    System.out.println("\n\nUser: " + userNameInFile +
                                       " is already logged in!\n\n");
                }
                else
                {
                    System.out
                            .println("\n\nUser: " +
                                     userNameInFile +
                                     " is currently logged in. Please logout first if you wish to login as " +
                                     loginUserName + "\n\n");
                }
            }
            else
            {
                userName = loginUserName;
                writeToLoggedInUser(userName);
                System.out.println("\n\n" + format + " Welcome to Facebook " + format +
                                   "\n\nUser: " + loginUserName +
                                   " has succesfully logged in! \n\n");
            }
        }
    }

    /**
     * Method that represents the logout functionality of facebook, sets the
     * user field to the null to indicate that user has logged out of facebook.
     * Also the LoggedInUser file is also updated about the logout of the User.
     */
    @Override
    public void logout()
    {
        String temp;
        if (userName.equals(""))
        {
            if (readLoggedInUsers().equals(""))
            {
                System.out.println("\n\n " + format + " No user currently logged in! " +
                                   format + "\n\n");
            }
            else
            {
                temp = readLoggedInUsers();
                userName = "";
                writeToLoggedInUser(userName);
                System.out.println("\n\n" + format + " User " + temp +
                                   " successfully logged out " + format + "\n\n");
            }

        }
        else
        {
            temp = userName;
            userName = "";
            writeToLoggedInUser(userName);
            System.out.println("\n\n" + format + " User " + temp +
                               " successfully logged out " + format + "\n\n");
        }
    }

    /**
     * This method creates a new facebook user and initializes his account
     * related details (like messages, friend and pending Request files)
     * 
     * @param newUserName, the string name of the new User.
     */
    @Override
    public void createUser(final String newUserName)
    {
        
        CreateNewFacebookUser newUser = new CreateNewFacebookUser(node.getPaxosInvoker(), newUserName, this.node);
        newUser.start();
    }

    /**
     * This method sends friend Requests to the friend (given as input
     * parameter)
     * 
     * @param friendName, the string name of the friend to whom request is sent.
     */
    @Override
    public void sendFriendRequest(final String friendName)
    {
        if (!ensureUserIsLoggedIn())
        {
            System.out
                    .println("\n\n" +
                             format +
                             "  No user is logged in. Please login first in order to send a friend request! " +
                             format + " \n\n");
            return;
        }

        RemoteInvoker client = node.getPaxosInvoker();
        client.invoke(IServerFileIO.AppendContentsToFileMethodName, new Serializable[] {
                                 friendName + PendingFriendRequestsFileSuffix,
                                 userName + "\r\n"},
                         new IResultCallback()
                         {
                             public void resultCallback(Status status, Serializable result)
                             {
                                 if (success(status,
                                             "Failed to send the friend Request to the Friend"))
                                     System.out.println(" \n\n " + format +
                                                        " Friend Request sent to " +
                                                        friendName + " " + format + "\n\n ");
                             }
                         });

    }

    /**
     * This method accepts the friend request sent by friend (the input
     * parameter) It first gets the "pendingFriendRequest file" of the friend
     * and deletes the username from it. Then it adds the username to the
     * "friend file" of the friend. Since its possible that user may have sent
     * friend request to the "friend" too. So we delete the friend name from the
     * "pendingFriendRequest file" of the user also. Then we add "friend" to the
     * "friend file" of the user.
     * 
     */
    @Override
    public void acceptFriendRequest(final String friendName)
    {

        if (!ensureUserIsLoggedIn())
        {
            System.out
                    .println("\n\n" +
                             format +
                             "  No user is logged in. Please login first in order to accept the friend Request! " +
                             format + " \n\n");
            return;
        }

        RemoteInvoker client = node.getPaxosInvoker();
        AcceptFriendCommand acceptReq = new AcceptFriendCommand(userName, friendName, client, client);
        acceptReq.start(null);
    }

    /**
     * This method reads and returns all the messages of the user present in his
     * Message file.
     */
    @Override
    public void readAllMessages()
    {

        if (!ensureUserIsLoggedIn())
        {
            System.out
                    .println("\n\n " + format + " No user is logged in. Please login first " +
                             format + " \n\n");
            return;
        }

        RemoteInvoker client = node.getPaxosInvoker();
        client.invoke(IServerFileIO.GetFileContentsMethodName,
                         new Serializable[] {userName + MessagesFileSuffix},
                         new IResultCallback()
                         {
                             @Override
                             public void resultCallback(Status status, Serializable result)
                             {
                                 if (status == Status.Success)
                                 {
                                     System.out.println("\n\n " + format +
                                                        "  All Messages of " + userName +
                                                        " are: " + format + " \n" +
                                                        result.toString() + " \n\n");
                                 }
                                 else
                                 {
                                     System.out.println("\n\n " + format +
                                                        "  Failed to read messages from " +
                                                        userName + format + " \n\n");
                                 }
                             }
                         });

    }

    /**
     * Checks if call was successful. If it was not - prints supplied message,
     * 
     * @param status Status that was returned
     * @param message Message to print.
     * @return boolean the success or failure depending on the status parameter.
     */
    protected boolean success(Status status, String message)
    {
        if (status == Status.Success)
            return true;

        // well no we know that we were not able to do something. Clean up.
        if (message != null)
            System.out.println("\n\n" + message + " \n\n");

        return false;
    }

    /**
     * This method posts the given message to all friends of the User.
     */
    @Override
    public void postMsgToAllFriends(final String message)
    {
        // ArgumentValidator.IsNotNullOrEmpty("message", message);
        assert message != null : "Message is null";

        if (!ensureUserIsLoggedIn())
        {
            System.out
                    .println("\n\n " + format + " No user is logged in. Please login first " +
                             format + " \n\n");
            return;
        }

        PostMessageToAllFriends postMsgToAll = new PostMessageToAllFriends(message, userName, this.node, this);
        postMsgToAll.start();
    }
}
